import WebSocket, { WebSocketServer } from 'ws';
import electron from "electron";

interface standardResponse {
  status?: Number;
  message?: String;
  data?: any;
  type?: string;
}

export class wsapi {
  static clients: any;
  port: any = 26369;
  wss: any = null;
  clients: any = [];
  private _win: any;

  constructor(win: any) {
    this._win = win;
  }

  createId() {
    // create random guid
    return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
      var r = (Math.random() * 16) | 0,
        v = c == "x" ? r : (r & 0x3) | 0x8;
      return v.toString(16);
    });
  }

  public async InitWebSockets() {
    electron.ipcMain.on("wsapi-updatePlaybackState", (_event: any, arg: any) => {
      this.updatePlaybackState(arg);
    });

    electron.ipcMain.on("wsapi-returnQueue", (_event: any, arg: any) => {
      this.returnQueue(JSON.parse(arg));
    });

    electron.ipcMain.on("wsapi-returnSearch", (_event: any, arg: any) => {
      console.log("SEARCH");
      this.returnSearch(JSON.parse(arg));
    });

    electron.ipcMain.on("wsapi-returnSearchLibrary", (_event: any, arg: any) => {
      this.returnSearchLibrary(JSON.parse(arg));
    });

    electron.ipcMain.on("wsapi-returnDynamic", (_event: any, arg: any, type: any) => {
      this.returnDynamic(JSON.parse(arg), type);
    });

    electron.ipcMain.on("wsapi-returnMusicKitApi", (_event: any, arg: any, method: any) => {
      this.returnMusicKitApi(JSON.parse(arg), method);
    });

    electron.ipcMain.on("wsapi-returnLyrics", (_event: any, arg: any) => {
      this.returnLyrics(JSON.parse(arg));
    });
    electron.ipcMain.on("wsapi-returnvolumeMax", (_event: any, arg: any) => {
      this.returnmaxVolume(JSON.parse(arg));
    });
    electron.ipcMain.on("wsapi-libraryStatus", (_event: any, inLibrary: boolean, rating: number) => {
      this.returnLibraryStatus(inLibrary, rating);
    });
    electron.ipcMain.on("wsapi-rate", (_event: any, kind: string, id: string, rating: number) => {
      this.returnRatingStatus(kind, id, rating);
    });
    electron.ipcMain.on("wsapi-change-library", (_event: any, kind: string, id: string, shouldAdd: boolean) => {
      this.returnLibraryChange(kind, id, shouldAdd);
    });
    this.wss = new WebSocketServer({
      port: this.port,
      perMessageDeflate: {
        zlibDeflateOptions: {
          // See zlib defaults.
          chunkSize: 1024,
          memLevel: 7,
          level: 3,
        },
        zlibInflateOptions: {
          chunkSize: 10 * 1024,
        },
        // Other options settable:
        clientNoContextTakeover: true, // Defaults to negotiated value.
        serverNoContextTakeover: true, // Defaults to negotiated value.
        serverMaxWindowBits: 10, // Defaults to negotiated value.
        // Below options specified as default values.
        concurrencyLimit: 10, // Limits zlib concurrency for perf.
        threshold: 1024, // Size (in bytes) below which messages
        // should not be compressed if context takeover is disabled.
      },
    });
    console.log(`WebSocketServer started on port: ${this.port}`);

    const defaultResponse: standardResponse = {
      status: 0,
      data: {},
      message: "OK",
      type: "generic",
    };

    this.wss.on("connection", (ws: any) => {
      ws.id = this.createId();
      console.log(`Client ${ws.id} connected`);
      this.clients.push(ws);
      ws.on("message", function incoming(_message: any) {});
      // ws on message
      ws.on("message", (message: any) => {
        let data = JSON.parse(message);
        let response: standardResponse = {
          status: 0,
          data: {},
          message: "OK",
          type: "generic",
        };
        if (data.action) {
          data.action.toLowerCase();
        }
        switch (data.action) {
          default:
            response.message = "Action not found";
            break;
          case "identify":
            response.message = "Thanks for identifying!";
            response.data = {
              id: ws.id,
            };
            ws.identity = {
              name: data.name,
              author: data.author,
              description: data.description,
              version: data.version,
            };
            break;
          case "play-next":
            this._win.webContents.executeJavaScript(`wsapi.playNext(\`${data.type}\`,\`${data.id}\`)`);
            response.message = "Play Next";
            break;
          case "play-later":
            this._win.webContents.executeJavaScript(`wsapi.playLater(\`${data.type}\`,\`${data.id}\`)`);
            response.message = "Play Later";
            break;
          case "quick-play":
            this._win.webContents.executeJavaScript(`wsapi.quickPlay(\`${data.term}\`)`);
            response.message = "Quick Play";
            break;
          case "get-lyrics":
            this._win.webContents.executeJavaScript(`wsapi.getLyrics()`);
            break;
          case "shuffle":
            this._win.webContents.executeJavaScript(`wsapi.toggleShuffle()`);
            break;
          case "set-shuffle":
            if (data.shuffle == true) {
              this._win.webContents.executeJavaScript(`MusicKit.getInstance().shuffleMode = 1`);
            } else {
              this._win.webContents.executeJavaScript(`MusicKit.getInstance().shuffleMode = 0`);
            }
            break;
          case "repeat":
            this._win.webContents.executeJavaScript(`wsapi.toggleRepeat()`);
            break;
          case "set-repeat":
            if (data.repeat === 0 || data.repeat === 1 || data.repeat === 2) {
              this._win.webContents.executeJavaScript(`MusicKit.getInstance().repeatMode = ${data.repeat}`);
              response.message = "Repeat " + data.repeat;
            } else {
              this._win.webContents.executeJavaScript(`wsapi.toggleRepeat()`);
              response.message = "Invalid Repeat, toggling";
            }
            break;
          case "seek":
            this._win.webContents.executeJavaScript(`MusicKit.getInstance().seekToTime(${parseFloat(data.time)})`);
            response.message = "Seek";
            break;
          case "pause":
            this._win.webContents.executeJavaScript(`MusicKit.getInstance().pause()`);
            response.message = "Paused";
            break;
          case "playpause":
            this._win.webContents.executeJavaScript(`MusicKitInterop.playPause()`);
            response.message = "Play/Pause";
            break;
          case "play":
            this._win.webContents.executeJavaScript(`MusicKit.getInstance().play()`);
            response.message = "Playing";
            break;
          case "stop":
            this._win.webContents.executeJavaScript(`MusicKit.getInstance().stop()`);
            response.message = "Stopped";
            break;
          case "volumeMax":
            this._win.webContents.executeJavaScript(`wsapi.getmaxVolume()`);
            response.message = "maxVolume";
            break;
          case "volume":
            this._win.webContents.executeJavaScript(`MusicKit.getInstance().volume = ${parseFloat(data.volume)}`);
            response.message = "Volume";
            break;
          case "mute":
            this._win.webContents.executeJavaScript(`MusicKit.getInstance().mute()`);
            response.message = "Muted";
            break;
          case "unmute":
            this._win.webContents.executeJavaScript(`MusicKit.getInstance().unmute()`);
            response.message = "Unmuted";
            break;
          case "next":
            this._win.webContents.executeJavaScript(`MusicKitInterop.next()`);
            response.message = "Next";
            break;
          case "previous":
            this._win.webContents.executeJavaScript(`MusicKitInterop.previous()`);
            response.message = "Previous";
            break;
          case "musickit-api":
            this._win.webContents.executeJavaScript(`wsapi.musickitApi(\`${data.method}\`, \`${data.id}\`, ${JSON.stringify(data.params)} , ${data.library})`);
            break;
          case "musickit-library-api":
            break;
          case "set-autoplay":
            this._win.webContents.executeJavaScript(`wsapi.setAutoplay(${data.autoplay})`);
            break;
          case "queue-move":
            this._win.webContents.executeJavaScript(`wsapi.moveQueueItem(${data.from},${data.to})`);
            break;
          case "get-queue":
            this._win.webContents.executeJavaScript(`wsapi.getQueue()`);
            break;
          case "search":
            if (!data.limit) {
              data.limit = 10;
            }
            this._win.webContents.executeJavaScript(`wsapi.search(\`${data.term}\`, \`${data.limit}\`)`);
            break;
          case "library-search":
            if (!data.limit) {
              data.limit = 10;
            }
            this._win.webContents.executeJavaScript(`wsapi.searchLibrary(\`${data.term}\`, \`${data.limit}\`)`);
            break;
          case "show-window":
            this._win.show();
            break;
          case "hide-window":
            this._win.hide();
            break;
          case "play-mediaitem":
            this._win.webContents.executeJavaScript(`wsapi.playTrackById("${data.id}", \`${data.kind}\`)`);
            response.message = "Playing track";
            break;
          case "get-status":
            response.data = {
              isAuthorized: true,
            };
            response.message = "Status";
            break;
          case "get-currentmediaitem":
            this._win.webContents.executeJavaScript(`wsapi.getPlaybackState()`);
            break;
          case "library-status":
            this._win.webContents.executeJavaScript(`wsapi.getLibraryStatus("${data.type}", "${data.id}")`);
            break;
          case "rating":
            this._win.webContents.executeJavaScript(`wsapi.rate("${data.type}", "${data.id}", ${data.rating})`);
            break;
          case "change-library":
            this._win.webContents.executeJavaScript(`wsapi.changeLibrary("${data.type}", "${data.id}", ${data.add})`);
            break;
          case "quit":
            electron.app.quit();
            break;
        }
        ws.send(JSON.stringify(response));
      });

      ws.on("close", () => {
        // remove client from list
        this.clients.splice(wsapi.clients.indexOf(ws), 1);
        console.log(`Client ${ws.id} disconnected`);
      });
      ws.send(JSON.stringify(defaultResponse));
    });
  }

  sendToClient(_id: any) {
    // replace the clients.forEach with a filter to find the client that requested
  }

  updatePlaybackState(attr: any) {
    const response: standardResponse = {
      status: 0,
      data: attr,
      message: "OK",
      type: "playbackStateUpdate",
    };
    this.clients.forEach(function each(client: any) {
      client.send(JSON.stringify(response));
    });
  }

  returnMusicKitApi(results: any, method: any) {
    const response: standardResponse = {
      status: 0,
      data: results,
      message: "OK",
      type: `musickitapi.${method}`,
    };
    this.clients.forEach(function each(client: any) {
      client.send(JSON.stringify(response));
    });
  }

  returnDynamic(results: any, type: any) {
    const response: standardResponse = {
      status: 0,
      data: results,
      message: "OK",
      type: type,
    };
    this.clients.forEach(function each(client: any) {
      client.send(JSON.stringify(response));
    });
  }

  returnLyrics(results: any) {
    const response: standardResponse = {
      status: 0,
      data: results,
      message: "OK",
      type: "lyrics",
    };
    this.clients.forEach(function each(client: any) {
      client.send(JSON.stringify(response));
    });
  }

  returnSearch(results: any) {
    const response: standardResponse = {
      status: 0,
      data: results,
      message: "OK",
      type: "searchResults",
    };
    this.clients.forEach(function each(client: any) {
      client.send(JSON.stringify(response));
    });
  }

  returnSearchLibrary(results: any) {
    const response: standardResponse = {
      status: 0,
      data: results,
      message: "OK",
      type: "searchResultsLibrary",
    };
    this.clients.forEach(function each(client: any) {
      client.send(JSON.stringify(response));
    });
  }

  returnQueue(queue: any) {
    const response: standardResponse = {
      status: 0,
      data: queue,
      message: "OK",
      type: "queue",
    };
    this.clients.forEach(function each(client: any) {
      client.send(JSON.stringify(response));
    });
  }

  returnmaxVolume(vol: any) {
    const response: standardResponse = {
      status: 0,
      data: vol,
      message: "OK",
      type: "maxVolume",
    };
    this.clients.forEach(function each(client: any) {
      client.send(JSON.stringify(response));
    });
  }

  returnLibraryStatus(inLibrary: boolean, rating: number) {
    const response: standardResponse = {
      status: 0,
      data: {
        inLibrary,
        rating,
      },
      message: "OK",
      type: "libraryStatus",
    };
    this.clients.forEach(function each(client: any) {
      client.send(JSON.stringify(response));
    });
  }

  returnRatingStatus(kind: string, id: string, rating: number) {
    const response: standardResponse = {
      status: 0,
      data: { kind, id, rating },
      message: "OK",
      type: "rate",
    };
    this.clients.forEach(function each(client: any) {
      client.send(JSON.stringify(response));
    });
  }

  returnLibraryChange(kind: string, id: string, shouldAdd: boolean) {
    const response: standardResponse = {
      status: 0,
      data: { kind, id, add: shouldAdd },
      message: "OK",
      type: "change-library",
    };
    this.clients.forEach(function each(client: any) {
      client.send(JSON.stringify(response));
    });
  }
}
